using System;
using System.Collections.Generic;

using log4net;

using IBatisNet.DataMapper;
using IBatisNet.Common;

using GymManager.Exception;
using GymManager.Mechanism;

using GymManager.Persistence;
using GymManager.Persistence.DAO;
using GymManager.Persistence.VO;

namespace GymManager.Business
{
    /// <summary>
    /// Class responsible by providing services regarding student monthly fee
    /// </summary>
    public class MonthlyFeeServices
    {
        // logger instance
        private ILog _logManager = LogManager.GetLogger(typeof(MonthlyFeeServices));

        /// <summary>
        /// This method is responsible by generating monthly fee entries to each active student
        /// in database. A monthly fee must be generated every month in a specific day in according to 
        /// the student register.
        /// </summary>
        /// <see cref="GymManager.Exception.BusinessException"/>
        public void generatePendingMonthlyFee()
        {
            try
            {
                // retrieve all active students
                StudentDAO studentDAO = new StudentDAO();
                IList<Student> studentList = studentDAO.findAllActive();

                if (studentList != null && studentList.Count > 0)
                {
                    // common structures
                    MonthlyFeeDAO monthlyFeeDAO = new MonthlyFeeDAO();
                    StudentModalityDAO studentModalityDAO = new StudentModalityDAO();
                    MonthlyFee mostRecentMonthlyFeeVO = null;
                    MonthlyFee monthlyFeeVO = null;

                    // processing each student information
                    foreach(Student studentVO in studentList)
                    {
                        // getting the most recent monthly fee for the current student
                        mostRecentMonthlyFeeVO = monthlyFeeDAO.findMostRecentByStudent(studentVO.Id);

                        // check if the monthly fee has already been created to the current month
                        if (  mostRecentMonthlyFeeVO == null ||
                            ( mostRecentMonthlyFeeVO.DueDate.Year < DateTime.Now.Year || mostRecentMonthlyFeeVO.DueDate.Month < DateTime.Now.Month) 
                           )
                        {
                            // creating a new monthly fee for the current month
                            monthlyFeeVO = new MonthlyFee();
                            monthlyFeeVO.StudentId = studentVO.Id;
                            monthlyFeeVO.DueDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, studentVO.PaymentDate.Day);
                            monthlyFeeVO.IsPaid = false;
                            monthlyFeeVO.PayDate = null;
                            monthlyFeeVO.ReceiverUserId = null;
                            monthlyFeeVO.TotalPrice = studentModalityDAO.getStudentMonthlyFee(studentVO.Id);

                            // adding monthly fee
                            monthlyFeeDAO.create(monthlyFeeVO);
                        }
                    }
                }
            }
            catch (PersistenceException)
            {
                throw new BusinessException();
            }
        }

        /// <summary>
        /// This method is responsible by generating monthly fee entries to a given student
        /// in database. The monthly fees will be generated from a start date with a specific value a specific
        /// number of times before start date.
        /// </summary>
        /// <param name="Int32">student identifier</param>
        /// <param name="DateTime"> start date from when the monthly fees will be generated</param>
        /// <param name="Int32">number of monthly fees to be generated from start date</param>
        /// <param name="Double"> monthly fee value</param>
        /// <see cref="GymManager.Exception.BusinessException"/>
        public void generateMonthlyFee(Int32 studentId, DateTime startDate, Int32 numberOfMonthlyFee, Double monthlyFeeValue)
        {
            try
            {
                // common structures
                MonthlyFeeDAO monthlyFeeDAO = new MonthlyFeeDAO();
                MonthlyFee monthlyFeeVO = null;

                // setting due date
                DateTime dueDate = startDate;

                // doing job inside transaction
                ISqlMapper ibatisMapper = DAOManager.getInstance();
                using (IDalSession sessionTransaction = ibatisMapper.BeginTransaction())
                {
                    // processing each student information
                    for (int i = 0; i < numberOfMonthlyFee; i++)
                    {
                        // creating a new monthly fee for the current month
                        monthlyFeeVO = new MonthlyFee();
                        monthlyFeeVO.StudentId = studentId;
                        monthlyFeeVO.DueDate = dueDate;
                        monthlyFeeVO.IsPaid = false;
                        monthlyFeeVO.PayDate = null;
                        monthlyFeeVO.ReceiverUserId = null;
                        monthlyFeeVO.TotalPrice = monthlyFeeValue;

                        // adding monthly fee
                        monthlyFeeDAO.create(monthlyFeeVO);

                        // upgrading monthly fee due date
                        dueDate = dueDate.AddMonths(1);
                    }

                    // commit the transaction
                    sessionTransaction.Complete();
                }
            }
            catch (PersistenceException)
            {
                throw new BusinessException();
            }
        }

        /// <summary>
        /// Method responsible by finding monthly fees associated to some 
        /// student in given year
        /// </summary>
        /// <param name="Int32"> student identifier </param>
        /// <param name="Int32"> monthly fee base year </param>
        /// <returns>IList - list of all monthly fees matching with the given criteria </returns>
        /// <see cref="GymManager.Exception.BusinessException"/>
        public IList<MonthlyFee> findByStudentAndYear(Int32 studentId, Int32 monthlyFeeBaseYear)
        {
            if (_logManager.IsInfoEnabled)
            {
                _logManager.Info("Begin Method");
            }

            IList<MonthlyFee> monthlyFeeList = null;

            try
            {
                // getting information about monthly fee
                MonthlyFeeDAO monthlyFeeDAO = new MonthlyFeeDAO();
                monthlyFeeList = monthlyFeeDAO.findByStudentAndYear(studentId, monthlyFeeBaseYear);
            }
            catch (PersistenceException)
            {
                throw new BusinessException();
            }

            if (_logManager.IsInfoEnabled)
            {
                _logManager.Info("End Method");
            }

            return monthlyFeeList;
        }

        /// <summary>
        /// Method responsible by removing a monthly fee from database
        /// </summary>
        /// <param name="Int32">monthly fee identifier</param>
        /// <see cref="GymManager.Exception.BusinessException"/>
        public void remove(Int32 monthlyFeeId)
        {
            if (_logManager.IsInfoEnabled)
            {
                _logManager.Info("Begin Method");
            }

            try
            {
                // removing monthly fee from database
                MonthlyFeeDAO monthlyFeeDAO = new MonthlyFeeDAO();
                monthlyFeeDAO.remove(monthlyFeeId);
            }
            catch (PersistenceException)
            {
                throw new BusinessException();
            }

            if (_logManager.IsInfoEnabled)
            {
                _logManager.Info("End Method");
            }
        }

        /// <summary>
        /// Method responsible by marking as paid the student's month fee.
        /// </summary>
        /// <param name="IList">monthly fee id list</param>
        /// <param name="Int32">user identifier who received the monthly fee payment</param>
        /// <see cref="GymManager.Exception.BusinessException"/>
        public void markMonthlyFeeAsPaid(IList<Int32> monthlyFeeIdList, Int32 receiverUserId)
        {
            if (_logManager.IsInfoEnabled)
            {
                _logManager.Info("Begin Method");
            }

            try
            {
                // updating information about monthly fee payment
                MonthlyFeeDAO monthlyFeeDAO = new MonthlyFeeDAO();
                monthlyFeeDAO.updateMonthlyFeeListAsPaid(monthlyFeeIdList, receiverUserId);
            }
            catch (PersistenceException)
            {
                throw new BusinessException();
            }

            if (_logManager.IsInfoEnabled)
            {
                _logManager.Info("End Method");
            }
        }
     }
}
